/*!
 * \file cron_expression.h
 *
 * \author cbh
 * \date 九月 2018
 *
 * QuartZ Cron表达式解析器
 */
#pragma once

#include <string>
#include <map>
#include <set>
#include <memory>
#include <ctime>

namespace gsf {

	struct ValueSet {
	public:
		int value;
		int pos;
	};

	struct DateTime {

	public:
		DateTime();
		DateTime(const DateTime& d);
	private:
		struct tm mTM;
		time_t mTime;
		void OnChanged();
	public:
		int GetYear();
		int GetMonth();
		int GetDaysOfMonth();
		int GetDaysOfWeek();
		int GetHoursOfDay();
		int GetMinutes();
		int GetSeconds();

		int64_t GetTotalSeconds();
		int64_t GetTotalMilliSeconds();

		void SetYear(int v);
		void SetMonth(int v);
		void SetDaysOfMonth(int v);
		void SetHoursOfDay(int v);
		void SetMinutes(int v);
		void SetSeconds(int v);

		void SetCalendarHour(int hr)
		{
			SetHoursOfDay(hr);
			if (GetHoursOfDay() != hr && hr != 24) {
				SetHoursOfDay(hr + 1);
			}
		}

		void AddSeconds(int diff);
		void AddYear(int y);
		bool Before(const DateTime& d);
		std::string ToString();
	};

	typedef std::shared_ptr<ValueSet> ValueSetPtr;
	typedef std::shared_ptr<DateTime> DateTimePtr;

	class CronExpression {
	protected:
		static const int SECOND;
		static const int MINUTE;
		static const int HOUR;
		static const int DAY_OF_MONTH;
		static const int MONTH;
		static const int DAY_OF_WEEK;
		static const int YEAR;
		static const int ALL_SPEC_INT; // '*'
		static const int NO_SPEC_INT; // '?'
		static const int ALL_SPEC;
		static const int NO_SPEC;

		static std::map<std::string, int> monthMap;
		static std::map<std::string, int> dayMap;
		static int MAX_YEAR;
	public:
		static void InternalInit();		
		// throws const std::string&
		CronExpression(const std::string& cronExpression);
	protected:
		void BuildExpression();
		void AddToSet(int val, int end, int incr, int type);
		void CheckIncrementRange(int incr, int type, int idxPos);

		std::set<int>& GetSet(int type);
		int StoreExpressionVals(int pos, std::string s, int type);
		int GetMonthNumber(const std::string& s);
		int GetDayOfWeekNumber(const std::string& s);
		int CheckNext(int pos, const std::string& s, int val, int type);
	public:
		// throws const std::string&
		DateTimePtr GetTimeAfter(DateTime afterTime);
	private:
		std::string mCronExpression;
		std::set<int> mSeconds;
		std::set<int> mMinutes;
		std::set<int> mHours;
		std::set<int> mDaysOfMonth;
		std::set<int> mMonths;
		std::set<int> mDaysOfWeek;
		std::set<int> mYears;

		int mNthDayOfWeek = 0;
		int mLastdayOffset = 0;
		bool mLastdayOfWeek = false;
		bool mLastdayOfMonth = false;
		bool mNearestWeekday = false;
		bool mExpressionParsed = false;
	};

	

}